#include "midi.h"

/**
 * Pointer vers le sequenceur MIDI
 * @var snd_seq_t*
 */
snd_seq_t *seq_handle;
/**
 * Association entre les canaux d'entrées et de sorties
 * @var int[16]
 */
char outputChannels[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
/**
 * Ports d'entrées
 * @var int[16]
 */
char inputPorts[16];
/**
 * Ports de sorties
 * @var int[16]
 */
char outputPorts[16];

/**
 * Intialise une exception midi
 * @param unsigned int code Le code d'erreur de l'exception
 * @param int val La valeur de l'exception
 * @param char* msg Le message associé à l'exception
 * @return midi_exception*
 */
midi_exception *midi_exception_init(unsigned int code, int val, char* msg) {
	midi_exception *e = malloc(sizeof(struct midi_exception));
	e->code=code;
	e->msg=msg;
	e->val=val;
	
	return e;
}

/**
 * Initialise le sequenceur MIDI
 * @param char* hw_name Le nom ALSA du périphérique utilisé
 * @param char* cli_name Le nom donné à l'application cliente
 * @return midi_exception* NULL si aucune erreur, sinon l'erreur
 */
midi_exception *midi_initialize(char *hw_name, char *cli_name) {
	int err,i,npfd;
	char buf[20];
	struct pollfd *pfd;
	
	/* Ouverture du séquenceur en mode entrée/sortie */
	err = snd_seq_open(&seq_handle, hw_name, SND_SEQ_OPEN_DUPLEX, 0);
	if(err < 0) return midi_exception_init(MIDI_ERR_SEQ_OPEN, err, "Erreur d'ouverture de séquenceur");
	
	/* Mise à jour du nom de l'application cliente */
	err = snd_seq_set_client_name(seq_handle, cli_name);
	if(err < 0) return midi_exception_init(MIDI_ERR_SEQ_SET_CLIENT_NAME, err, "Erreur de mise à jour du nom d'application cliente");
	
	/* Ouverture des ports d'entrée */
	for(i=0;i<16;i++) {
		sprintf(buf, "MIDI Input %d", i+1);
		inputPorts[i] = snd_seq_create_simple_port(seq_handle, buf, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION);
		if(inputPorts[i] <= 0) return midi_exception_init(MIDI_ERR_SEQ_CREATE_INPUT_PORT, i+1, "Erreur d'ouverture du port d'entrée");
	}
	
	/* Ouverture des ports de sortie */
	for(i=0;i<16;i++) {
		sprintf(buf, "MIDI Output %d", i+1);
		outputPorts[i] = snd_seq_create_simple_port(seq_handle, buf, SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION);
		if(outputPorts[i] <= 0) return midi_exception_init(MIDI_ERR_SEQ_CREATE_OUTPUT_PORT, i+1, "Erreur d'ouverture du port de sortie");
	}
	
	/* Bonne question... Je pense qu'il s'agit ici de récupérer la liste des commandes midi comprise par le périphérique d'entrée. */
	npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
	if(npfd <= 0) return midi_exception_init(MIDI_ERR_SEQ_POLL_DESCRIPTORS_COUNT, npfd, "");
	pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
	err = snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN);
	if(err <= 0) return midi_exception_init(MIDI_ERR_SEQ_POLL_DESCRIPTORS, err, NULL);
	
	return NULL;
}

/**
 * Définie un port de sortie pour un port d'entrée donné
 * @param unsigned char input_channel Le port d'entrée
 * @param unsigned char output_channel Le port de sortie
 * @return unsigned char La modification a bien été effectuée
 */
unsigned char set_output_channel(unsigned char input_channel, char output_channel) {
	if(input_channel >= 16 || output_channel >= 16) return 0;
	outputChannels[input_channel] = output_channel<0?-1:output_channel;
	return 1;
}
/**
 * Retourne le port de sortie d'un port d'entrée donné
 * @param unsigned char input_channel Le port d'entrée
 * @return unsigned char Le port de sortie associé
 */
unsigned char get_output_channel(unsigned char input_channel) {
	if(input_channel >= 16) return -2;
	return outputChannels[input_channel];
}

/**
 * Liste des périphérques MIDI de sortie
 * @return char** La liste des périphériques MIDI de sortie
 */
char **get_midi_output_hw() {
	int status, midCount;
	
	status = ioctl(seqfd, SNDCTL_SEQ_NRMIDIS, &midiCount);
}
/**
 * Liste des pérphériques MIDI d'entrée
 * @return char** La liste des périphériques MIDI d'entrée
 */
char **get_midi_input_hw();
